type typeCallback = (currentDate: number[]) => void;

//定义的IDatePicker接口具有两个方法
interface IDatePicker {
  getCurrentDate(): number[];
  watch(callback: typeCallback): void;
}

enum DateMark {
  YEAR = 'YEAR',
  MONTH = 'MONTH',
  DAY = 'DAY'
}

//一般来讲，一个类只能继承自另一个类，有时候不同类之间可以有一些共有的特性，这时候就可以把特性提取成接口（interfaces），用 implements 关键字来实现。
export default class DatePicker implements IDatePicker {
  private yearArr: number[];
  //默认值为指定数组元素的数值数组
  private monthArr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
  private dayArr: number[];
  //默认值为仅限3位的数值数组
  private currentDate: number[] = new Array(3) as number[];
  private callback: typeCallback;

  private oYearWrapper: HTMLElement = document.querySelector('.year-wrapper')!;
  private oMonthWrapper: HTMLElement =
    document.querySelector('.month-wrapper')!;
  private oDayWrapper: HTMLElement = document.querySelector('.day-wrapper')!;

  constructor(yearArr: number[], currentDate: number[]) {
    this.yearArr = yearArr;
    this.currentDate = currentDate;
    this.dayArr = this.createDayArr(currentDate);
    //初始化节点
    this.initDom();
    this.bindEvent();
  }

  //创造一个数组包含一个月中的所有天数
  private createDayArr([year, month]: number[]): number[] {
    const count = getDayCount(year, month);
    const arr: number[] = [];
    for (let i = 1; i <= count; i++) {
      arr.push(i);
    }
    return arr;
  }

  //在类名为scroll-wrapper的节点下插入多个子项节点元素
  private createItems(wrapper: HTMLElement, arr: number[]): void {
    const oFrag = document.createDocumentFragment();
    //获取类名为scroll-wrapper的节点
    const oScrollWrapper = wrapper.querySelector('.scroll-wrapper')!;
    //首先头部创建两个空白的子项节点
    for (let i = 0; i < 2; i++) {
      const oItem = createPlaceHolder();
      oFrag.appendChild(oItem);
    }

    //中间动态的创建子项节点
    arr.forEach((year) => {
      const oItem = createPlaceHolder();
      oItem.innerText = year + '';
      oFrag.appendChild(oItem);
    });

    //尾部创建两个空白的子项节点
    for (let i = 0; i < 2; i++) {
      const oItem = createPlaceHolder();
      oFrag.appendChild(oItem);
    }

    //每次追究节点前确保该父节点是无内容的
    oScrollWrapper.innerHTML = '';
    oScrollWrapper.appendChild(oFrag);
  }

  private initDom() {
    this.createItems(this.oYearWrapper, this.yearArr);
    this.createItems(this.oMonthWrapper, this.monthArr);
    this.createItems(this.oDayWrapper, this.dayArr);
    this.initCurrentDate(this.currentDate);
  }

  //根据用户传入的日期实现页面初始化时自动滚动定位到相应的日期位置
  private initCurrentDate([year, month, day]: number[]) {
    // console.log(year, month, day); 用户传的日期 2021, 2, 19
    const yearIndex = this.yearArr.indexOf(year);
    const monthIndex = this.monthArr.indexOf(month);
    const dayIndex = this.dayArr.indexOf(day);

    setScrollY(this.oYearWrapper, yearIndex);
    setScrollY(this.oMonthWrapper, monthIndex);
    setScrollY(this.oDayWrapper, dayIndex);
  }

  private bindEvent() {
    this.oYearWrapper.addEventListener(
      'scrollend',
      this.setCurrentDate.bind(this, this.oYearWrapper, 'YEAR'),
      false
    );
    this.oMonthWrapper.addEventListener(
      'scrollend',
      this.setCurrentDate.bind(this, this.oMonthWrapper, 'MONTH'),
      false
    );
    this.oDayWrapper.addEventListener(
      'scrollend',
      this.setCurrentDate.bind(this, this.oDayWrapper, 'DAY'),
      false
    );
  }

  //监听当前滚动条，当结束滚动时
  private setCurrentDate(wrapper: HTMLElement, field: DateMark) {
    //获取当前高度并计算出当前子项元素的索引
    const index = wrapper.scrollTop / 50;

    //根据对应的滚动条事件对当前日期进行更新修改
    switch (field) {
      case DateMark.YEAR:
        this.currentDate[0] = this.yearArr[index];
        //根据年份月份重新计算并更新当月天数的数组
        this.dayArr = this.createDayArr(this.currentDate);
        //当年份和月份都改变时及时更新日份的天数并渲染到视图
        this.createItems(this.oDayWrapper, this.dayArr);
        this.callback && this.callback(this.currentDate);
        break;
      case DateMark.MONTH:
        this.currentDate[1] = this.monthArr[index];
        this.dayArr = this.createDayArr(this.currentDate);
        this.createItems(this.oDayWrapper, this.dayArr);
        this.callback && this.callback(this.currentDate);
        break;
      case DateMark.DAY:
        this.currentDate[2] = this.dayArr[index];
        this.dayArr = this.createDayArr(this.currentDate);
        this.createItems(this.oDayWrapper, this.dayArr);
        this.callback && this.callback(this.currentDate);
        break;
      default:
        break;
    }
  }

  public getCurrentDate(): number[] {
    return this.currentDate;
  }

  public watch(callback: typeCallback): void {
    typeof callback === 'function' && (this.callback = callback);
  }
}

//根据年份和月份计算当前月份的天数
function getDayCount(year: number, month: number): number {
  return new Date(year, month, 0).getDate();
}

//创建一个空白子项节点元素
function createPlaceHolder(): HTMLElement {
  const oItem = document.createElement('div');
  oItem.classList.add('item');
  return oItem;
}

//根据索引*子项元素高度计算出距离顶部的距离并滚动至指定位置
function setScrollY(wrapper: HTMLElement, index: number) {
  //滚动至(x, y)
  wrapper.scrollTo(0, index * 50);
}
